import 'dart:collection';
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
// ignore: depend_on_referenced_packages
import 'package:http_parser/http_parser.dart' show MediaType;
import 'package:sample2/components/notification/user_info.dart';
import 'package:sample2/helper/app_helper.dart';
import 'package:sample2/utils/json_util.dart';
import 'package:sample2/utils/string_util.dart';

class NetworkResponseObject extends Object {
  int? statusCode = 0;
  Map? content = {};
  int? respondCode = 0;
  String? responseMsg = "";

  NetworkResponseObject(
      {this.content,
      required this.respondCode,
      required this.responseMsg,
      this.statusCode});
  NetworkResponseObject.withContent({required this.content});

  @override
  String toString() {
    return JsonUtil.jsonStringFromJsonMap(HashMap.from({
      "statusCode": statusCode,
      "content": content,
      "responseCode": respondCode,
      "responseMsg": responseMsg
    }));
  }
}

typedef AppLogoutListener = void Function();

class NetworkUtil extends Object {
  static HashMap<int, AppLogoutListener> logoutListeners = HashMap();

  static int registerLogoutListener(AppLogoutListener logoutListener) {
    int key = DateTime.now().microsecondsSinceEpoch;
    logoutListeners.putIfAbsent(key, () => logoutListener);
    return key;
  }

  static removeLogoutListener(int key) {
    if (logoutListeners.containsKey(key)) {
      logoutListeners.remove(key);
    }
  }

  static VoidCallback?  dissmissUI;
//TODO: change the base url by env variable.
  static var options = 
      BaseOptions(baseUrl: AppHelper.shared.getBkDomainName()+"/api/", connectTimeout: 5000);
  static Dio dio = Dio(options);

  static Future<NetworkResponseObject> get(
      String path, Map<String, String>? headers) {
    return dio
        .get(path,
            options: Options(headers: buildHeaders(headers), method: "GET"))
        .then((Response value) {
      NetworkResponseObject response = processResponse(value);
      debugPrint("$path -> got get response : ${response.toString()}");
      return response;
    }).catchError((error) {
      return processError(error);
    });
  }

  static Future<NetworkResponseObject> post(
      String path, Map<String, String>? headers, Map? body) {
    return dio
        .post(path,
            data: jsonEncode(body),
            options: Options(headers: buildHeaders(headers), method: "POST"))
        .then((Response value) {
      NetworkResponseObject response = processResponse(value);
      debugPrint("$path -> got get response : ${response.toString()}");
      return response;
    }).catchError((error) {
     return processError(error);
    });
  }

  static Future<NetworkResponseObject> uploadFile(String uri,
      {String? filename,
      String? filePath,
      String? type,
      String? subType}) async {
    assert(filePath != null);
    assert(type != null);
    assert(subType != null);
    return NetworkResponseObject(
        content: {}, respondCode: 200, responseMsg: "");

    // var request = dio.("POST", requestUri);
    // request.fields["filename"] = filename ?? "afile";
    // request.fields["userid"] =
    //     (await AppHelper.shared.getUserInfo())?.userid ?? "";

    // http.MultipartFile file = await http.MultipartFile.fromPath(
    //     "field", filePath!,
    //     filename: filename, contentType: MediaType(type!, subType!));
    // request.files.add(file);
    // //TODO:檢查有效性， upload file
    // return request.send().then((response) {
    //   return NetworkResponseObject(
    //       content: {}, respondCode: 200, responseMsg: "");
    // }).catchError((error) {
    //   debugPrint(error);
    // });
  }

  static Future<NetworkResponseObject> put(
      String path, Map<String, String>? headers, Map? body) {
    return dio
        .put(path,
            data: jsonEncode(body),
            options: Options(headers: buildHeaders(headers), method: "PUT"))
        .then((Response value) {
      NetworkResponseObject response = processResponse(value);
      debugPrint("$path -> got get response : ${response.toString()}");
      return response;
    }).catchError((error) {
     return processError(error);
    });
  }

  static Future<NetworkResponseObject> delete(
      String path, Map<String, String>? headers, Map? body) {
    return dio
        .delete(path,
            data: jsonEncode(body),
            options: Options(headers: buildHeaders(headers), method: "DELETE"))
        .then((Response value) {
      NetworkResponseObject response = processResponse(value);
      debugPrint("$path -> got get response : ${response.toString()}");
      return response;
    }).catchError((error) {
     return processError(error);
    });
  }

  static Map<String, String> buildHeaders(Map<String, String>? headers) {
    //需使用標準的請求頭
    Map<String, String> defaultHeaders = {
      HttpHeaders.contentTypeHeader: "application/json",
      HttpHeaders.acceptHeader: "application/json",
      HttpHeaders.authorizationHeader:
       StringUtil.isNullOrEmpty(AppHelper.shared.userInfo?.token) ? "" : "Bearer ${AppHelper.shared.userInfo?.token}",
    };
    Map<String, String> fixedHeaders = HashMap.from(defaultHeaders);
    if (headers != null && headers.isNotEmpty) {
      fixedHeaders.addAll(headers);
    }
    debugPrint("send request headers ${jsonEncode(fixedHeaders)}");
    return fixedHeaders;
  }

  static processError(DioError error) {
    debugPrint(error.toString());
    dissmissUI?.call();
    if (error.response?.statusCode == 401) {
      for (AppLogoutListener callback in logoutListeners.values) {
        callback.call();
        //TODO: 401之后 跳转登录页面
      }
    }else{
      if(error.response != null){
        NetworkResponseObject response = processResponse(error.response!);
        return response;
      }
    }
    return NetworkResponseObject( statusCode:error.response?.statusCode ,respondCode: -1, responseMsg: error.response?.statusMessage);
  }

  static processResponse(Response resVal) {
    var responsedObj = resVal.data as Map;
    var responseCode = responsedObj["code"] ?? resVal.statusCode;
    var responseMsg = responsedObj["message"];
    Map responseContent = {"msg":resVal.statusMessage!};
    if(responsedObj.containsKey("content")){
      responseContent = responsedObj["content"];
    }

    NetworkResponseObject response = NetworkResponseObject(
        statusCode: resVal.statusCode,
        content: responseContent,
        respondCode: responseCode,
        responseMsg: responseMsg);
    return response;
  }
}
